﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CalcSharp.Core.Containers
{
    /// <summary>
    /// deviation type enumeration
    /// </summary>
    public enum deviattype : byte
    {
        normal,
        corigated
    }
    /// <summary>
    /// Average type enumeration
    /// </summary>
    public enum avgtype : byte
    {
        normal,
        mertani,
        harmonic,
        square
    }

    [Serializable]
    public abstract class ASet: ICloneable
    {
        public double this[int index]
        {
            get { return this.ValueAt(index); }
            set { this.ValueAt(index, value); }
        }

        protected abstract void ValueAt(int index, double value);
        protected abstract double ValueAt(int index);

        protected double _entropy;

        public double Entropy
        {
            get { return _entropy; }
            set { _entropy = value; }
        }

        public bool isNull()
        {
            return this.Count > 0 ? false : true;
        }

        #region Basic Features
        public abstract int Count
        {
            get;
        }
        public abstract double[] ToArray();
        public abstract bool Contains(double value);
        public abstract void AddItem(double itm);
        public abstract void DeleteItem(int index);
        public abstract ASet CreateSet(int Count);
        public abstract ASet Randomize();
        public abstract ASet Unique();
        public abstract ASet Sort();
        public abstract ASet Reverse();
        #endregion

        #region ICloneable Members

        object ICloneable.Clone()
        {
            return Clone();
        }
        public virtual ASet Clone()
        {
            ASet ret = CreateSet(Count);
            for (int i = 0; i < this.Count; i++)
            {
                ret[i] = ValueAt(i);
            }
            return ret;
        }

        #endregion

        #region statistics
        /// <summary>
        /// Returns the greatest element
        /// </summary>
        /// <returns>the greatest element</returns>
        public double StatMax()
        {
            int i;
            double max = 0.0;
            max = this[0];
            for (i = 0; i < this.Count; i++)
            {
                if (this[i] > max) max = this[i];
            }
            return max;
        }

        /// <summary>
        /// Returns the smallest element
        /// </summary>
        /// <returns>the smallest element</returns>
        public double StatMin()
        {
            int i;
            double min = 0.0;
            min = this.ValueAt(0);
            for (i = 0; i < this.Count; i++)
            {
                if (this[i] < min) min = this[i];
            }
            return min;
        }

        public double StatSum()
        {
            int i;
            double sum = 0.0;
            for (i = 0; i < this.Count; i++)
            {
                sum += this[i];
            }
            return sum;
        }

        /// <summary>
        /// Returns the average of the set
        /// </summary>
        /// <param name="t">an average type</param>
        /// <returns>the average</returns>
        public double StatAvg(avgtype t)
        {
            double avg = 0.0;
            switch (t)
            {
                case avgtype.normal:
                    avg = this.StatSum() / this.Count;
                    return avg;
                case avgtype.mertani:
                    avg = 1.0;
                    for (int i = 0; i < this.Count; i++) avg *= this[i];
                    return Math.Pow(avg, 1 / this.Count);
                case avgtype.harmonic:
                    avg = 0.0;
                    for (int i = 0; i < this.Count; i++) avg += (1 / this[i]);
                    return this.Count / avg;
                case avgtype.square:
                    avg = 0.0;
                    for (int i = 0; i < this.Count; i++) avg += Math.Pow(this[i], 2);
                    return avg / this.Count;
                default:
                    return 0;
            }
        }

        public double StatDeviat(deviattype t)
        {
            double temp = 0.0;
            double atlag = this.StatAvg(avgtype.normal);
            for (int i = 0; i < this.Count; i++) temp += Math.Pow(Convert.ToDouble(this[i]) - atlag, 2);
            if (t == deviattype.normal) return temp / this.Count;
            else return temp / (this.Count - 1);
        }

        public double Stat_Range()
        {
            return this.StatMax() - this.StatMin();
        }

        public double StatModus()
        {
            Dictionary<double, long> d = new Dictionary<double, long>();
            double maxkey = 0.0;
            long maxval = 0;
            for (int i = 0; i < this.Count; i++)
            {
                if (d.ContainsKey(this[i])) d[this[i]]++;
                else d.Add(this[i], 1);
            }
            foreach (double key in d.Keys)
            {
                maxkey = key;
                maxval = d[key];
                break;
            }
            foreach (double key in d.Keys)
            {
                if (d[key] > maxval)
                {
                    maxval = d[key];
                    maxkey = key;
                }
            }
            return maxkey;
        }

        public double StatMedian()
        {
            ASet Copy = this.Clone();
            double median = 0;
            int fele = Convert.ToInt32(Copy.Count / 2);
            Copy.Sort();
            if (Copy.Count % 2 == 0)
            {
                median = Copy[fele] + Copy[fele - 1];
                return median / 2;
            }
            else
            {
                return Copy[fele];
            }
        }
        #endregion
    }
}
